package com.roadmap.biz.life.task;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Vector;

import org.hibernate.Hibernate;
import org.hibernate.HibernateException;

import com.roadmap.biz.common.BaseManager;
import com.roadmap.biz.common.RdcQueryConst;
import com.roadmap.common.BaseConst;
import com.roadmap.common.BaseEntity;
import com.roadmap.common.RdcResourceBundle;
import com.roadmap.common.BaseEnum.ActionType;
import com.roadmap.entities.Aspirant;
import com.roadmap.entities.LifeGoal;
import com.roadmap.entities.LifeTask;
import com.roadmap.entities.LimitedAccess;
import com.roadmap.entities.ext.TaskExt;
import com.roadmap.exception.ActionNotSupportedException;
import com.roadmap.exception.EntityNotFoundException;
import com.roadmap.exception.InvalidActionException;
import com.roadmap.exception.InvalidQueryException;
import com.roadmap.exception.InvalidValueException;
import com.roadmap.exception.NotAuthorizedException;
import com.roadmap.exception.NotMatchedException;
import com.roadmap.exception.NullObjectException;
import com.roadmap.exception.ServerException;
import com.roadmap.repo.db.RdcQuery;

public class TaskManager extends BaseManager {
	private final static Set<Integer> Valid_Goal_Status_Set = new HashSet<Integer>();
	
	static {
		Valid_Goal_Status_Set.add(new Integer(BaseConst.LIFEGOAL_STATUS_UNSCHEDULED));
		Valid_Goal_Status_Set.add(new Integer(BaseConst.LIFEGOAL_STATUS_UNDERGOING));
		Valid_Goal_Status_Set.add(new Integer(BaseConst.LIFEGOAL_STATUS_SCHEDULED));
	}

	public TaskManager(String currentUser, String itemOwner) {
		super(currentUser, itemOwner, LifeTask.class, Aspirant.class);
	}

	public TaskManager(String currentUser, String itemOwner, Class parentClazz) {
		super(currentUser, itemOwner, LifeTask.class, parentClazz);
	}

	@Override
	public TaskExt getEntityExt(String entityId) throws NullObjectException,
			InvalidQueryException, ServerException, InvalidValueException,
			EntityNotFoundException, NotMatchedException,
			NotAuthorizedException {
		if (entityId == null) {
			InvalidValueException e = new InvalidValueException();
			_log.error("NULL entity Id", e);
			throw e;
		}

		// 查询任务及其计划
		RdcQuery query = getDbUtil().createNativeSQLQuery(
				RdcQueryConst.SQL_TASKS_WITH_GOAL);
		query.addEntity("task", LifeTask.class);
		query.addScalar("name");
		query.setParameter("uid", getOwnerId());
		query.setParameter("tid", entityId);
		query.setParameter("state", BaseConst.LIFETASK_STATUS_DELETED);
		Object[] result = (Object[]) query.getUniqueResult();
		if (result == null) {
			EntityNotFoundException e = new EntityNotFoundException();
			_log.error("the specified task was not found.", e);
			throw e;
		}

		LifeTask task = (LifeTask) result[0];
		// 检查用户权限
		if (!checkUserAccess(task)) {
			NotAuthorizedException e = new NotAuthorizedException();
			_log.error(String.format("Access is not allowed fro user %s",
					getCurrentUserId()), e);
			throw e;
		}

		TaskExt taskExt = new TaskExt(task);
		taskExt.setGoalName((String) result[1]);
		// 任务相关统计量
		result = (Object[]) getDbUtil().getUniqueEntity(
				RdcQueryConst.SQL_TASK_STATISTICS, new Object[] { entityId });
		if (result != null) {
			taskExt.setExecCount(((Long) result[0]).intValue());
			if (result[1] != null) {
				taskExt.setTatalExecTime(((Long) result[1]).intValue());
			}
			if (result[2] != null) {
				taskExt.setProgress(((Long) result[2]).intValue());
			}
			taskExt.setLatestExecDate((Date) result[3]);
			taskExt.setSymptom(diagnose(task));
		}
		return taskExt;
	}

	/** 获取可执行的任务（scheduled和undergoing），并检查用户的访问权限 */
	@Override
	public List getExecutableEntities() throws HibernateException,
			ServerException, InvalidQueryException, InvalidValueException,
			NotMatchedException {
		// 获得状态为scheduled和undergoing的任务
		RdcQuery query = getDbUtil().createNativeSQLQuery(
				RdcQueryConst.SQL_TASK_WITH_GOAL_BY_STATUS);
		query.addEntity("task", LifeTask.class);
		query.addScalar("name");
		query.setParameters(new Object[] { getOwnerId(),
				BaseConst.LIFETASK_STATUS_SCHEDULED,
				BaseConst.LIFETASK_STATUS_UNDERGOING });
		List list = query.getResults();
		if (list == null || list.isEmpty()) {
			return null;
		}

		// 存放权限为limited的任务的ID
		List<String> limitedTaskIDs = new ArrayList<String>();
		// 存放当前用户可访问的任务
		Map<String, TaskExt> taskMap = new LinkedHashMap<String, TaskExt>();
		Iterator iter = list.iterator();
		while (iter.hasNext()) {
			Object[] result = (Object[]) iter.next();
			LifeTask task = (LifeTask) result[0];
			// 如果任务权限不是private或当前用户即任务拥有者，则将任务放入容器
			if (task.getVisibility() != BaseConst.ENTRY_VISIBILITY_PRIVATE
					|| getCurrentUserId().equals(getOwnerId())) {
				TaskExt ext = new TaskExt(task);
				ext.setGoalName((String) result[1]);
				taskMap.put(task.getId(), ext);

				// 若任务权限是limited，需进一步确认用户权限
				if (task.getVisibility() == BaseConst.ENTRY_VISIBILITY_LIMITED) {
					limitedTaskIDs.add(task.getId());
				}
			}
		}

		filterLimitedEntities(limitedTaskIDs.toArray(new String[0]), taskMap);
		computeTasksProgress(taskMap);
		return getResultsForSpecifiedPage(taskMap.values());
	}

	/**
	 * 计算各任务的进度等统计量
	 */
	private void computeTasksProgress(Map<String, TaskExt> taskMap)
			throws ServerException, NotMatchedException {
		if (taskMap == null || taskMap.isEmpty()) {
			return;
		}

		RdcQuery query = getDbUtil().createNativeSQLQuery(
				RdcQueryConst.SQL_TASKS_STATISTICS);
		query.setParameterList("taskIDs", taskMap.keySet().toArray());
		query.addScalar("tid", Hibernate.STRING);
		query.addScalar("amount", Hibernate.INTEGER);
		query.addScalar("totalTime", Hibernate.INTEGER);
		query.addScalar("progress", Hibernate.INTEGER);
		query.addScalar("created", Hibernate.DATE);
		List results = query.getResults();
		if (results == null || results.isEmpty()) {
			NotMatchedException e = new NotMatchedException();
			_log.error("No results returned for specified tasks. It's wrong!");
			throw e;
		}

		Date today = new Date();
		Iterator iter = results.iterator();
		while (iter.hasNext()) {
			Object[] result = (Object[]) iter.next();
			String taskId = (String) result[0];
			TaskExt ext = taskMap.get(taskId);
			if (ext == null) {
				_log.warn(String.format(
						"Statistics of task %s was not returned.", taskId));
				continue;
			}

			ext.setSymptom(diagnose(ext, today));

			// 从数据库返回的统计数据可能是null，即task没有执行记录
			if (result[1] == null) {
				continue;
			}
			ext.setExecCount(((Integer) result[1]).intValue());
			ext.setTatalExecTime(((Integer) result[2]).intValue());
			ext.setProgress(((Integer) result[3]).intValue());
			ext.setLatestExecDate((Date) result[4]);
		}
	}

	/** 诊断指定任务 */
	private String diagnose(LifeTask task) {
		return diagnose(task, new Date());
	}

	/** 诊断指定任务 */
	private String diagnose(LifeTask task, Date ref) {
		if (task == null || ref == null) {
			_log.warn("NULL inputs for task or the date");
			return null;
		}

		int status = task.getStatus();
		// only when status is undergoing or scheduled, validation is needed
		if (status == BaseConst.LIFETASK_STATUS_SCHEDULED
				|| status == BaseConst.LIFETASK_STATUS_UNDERGOING) {
			Date start = task.getScheduledStartDate();
			Date end = task.getScheduledEndDate();
			if (status == BaseConst.LIFETASK_STATUS_SCHEDULED
					&& start.before(ref)) {
				return RdcResourceBundle.Entity_Symptom_Not_Started;
			}

			if (status == BaseConst.LIFETASK_STATUS_UNDERGOING
					&& end.before(ref)) {
				return RdcResourceBundle.Entity_Symptom_Not_Ended;
			}
		}

		return RdcResourceBundle.Entity_Symptom_Healthy;
	}

	@Override
	public void create(BaseEntity entity) throws NullObjectException,
			InvalidActionException, InvalidValueException, ServerException,
			EntityNotFoundException, ActionNotSupportedException,
			NotAuthorizedException {
		checkEntityIsValid(entity);

		LifeTask task = (LifeTask) entity;
		// 如果任务的创建者未指定，则指定当前用户为创建者，否则检查任务的创建者是否是当前用户
		if (task.getAspirantId() == null) {
			task.setAspirantId(getCurrentUserId());
		}
		else {
			// 检查用户执行该操作的权限
			checkUserCUD(task.getAspirantId());
		}

		Vector<BaseEntity> entities = new Vector<BaseEntity>();
		entities.add(task);

		if (task.getGoalId() != null) {
			// 若任务属于某项计划，则先确认计划（目标）的状态是否合法
			LifeGoal goal = (LifeGoal) getDbUtil().getUniqueEntity(
					RdcQueryConst.HQL_GOAL_BY_USER_ID,
					new Object[] { task.getGoalId(), getOwnerId(),
					BaseConst.LIFEGOAL_STATUS_DELETED });

			checkUserCUD(goal.getAspirantId());

			// 检查计划（目标）的状态
			int status = goal.getStatus();
			if (Valid_Goal_Status_Set.contains(status) == false) {
				ActionNotSupportedException e = new ActionNotSupportedException();
				String msg = "Only scheduled or undergoing goals allow new tasks created";
				_log.error(msg, e);
				throw e;
			} else if(status == BaseConst.LIFEGOAL_STATUS_UNSCHEDULED) {
				goal.setStatus(BaseConst.LIFEGOAL_STATUS_SCHEDULED);
				entities.add(goal);
			}
		}

		// 如果任务是limited，则保存其权限设置
		if (task.getVisibility() == BaseConst.ENTRY_VISIBILITY_LIMITED) {
			entities.add(task);
			entities.add(new LimitedAccess(task.getId(), 
					task.getLimitedMembers()));
			
		}
		
		getDbUtil().saveOrUpdateEntities(entities);
		getNoticeManager().notifyForEntry(task, ActionType.CREATE);

		if (_log.isDebugEnabled()) {
			_log.debug("new task was created as " + task.getId());
		}
	}

	@Override
	public BaseEntity cancel(String entryId) throws NullObjectException,
			ServerException, InvalidValueException, EntityNotFoundException,
			InvalidActionException, NotAuthorizedException,
			ActionNotSupportedException {
		return changeStatus(entryId, BaseConst.LIFETASK_STATUS_CANCELED);
	}

	@Override
	public void delete(String entryId) throws NullObjectException,
			ServerException, InvalidValueException, InvalidActionException,
			EntityNotFoundException, NotAuthorizedException,
			ActionNotSupportedException {
		changeStatus(entryId, BaseConst.LIFETASK_STATUS_DELETED);
	}

	@Override
	public BaseEntity complete(String entryId) throws NullObjectException,
			ServerException, InvalidValueException, EntityNotFoundException,
			InvalidActionException, NotAuthorizedException,
			ActionNotSupportedException {
		return changeStatus(entryId, BaseConst.LIFETASK_STATUS_COMPLETED);
	}

	/**
	 * 修改任务状态。当删除任务时，只修改任务的状态值，不删掉任何数据
	 */
	private BaseEntity changeStatus(String entityId, int status)
			throws NullObjectException, ServerException, InvalidValueException,
			EntityNotFoundException, InvalidActionException,
			NotAuthorizedException, ActionNotSupportedException {
		LifeTask task = (LifeTask) get(entityId);
		if (task == null
				|| task.getStatus() == BaseConst.LIFETASK_STATUS_DELETED) {
			EntityNotFoundException e = new EntityNotFoundException();
			_log.error("Entity was not found for " + entityId, e);
			throw e;
		}

		// 检查用户执行该操作的权限
		checkUserCUD(task.getAspirantId());

		Date today = new Date();
		// 当目标状态时completed时
		if (status == BaseConst.LIFETASK_STATUS_COMPLETED) {
			// 只有进行中的任务才能被完成
			if (task.getStatus() == BaseConst.LIFETASK_STATUS_UNDERGOING) {
				// 任务当前更新时间即其完成时间
				task.setRealEndDate(today);
			}
			else {
				ActionNotSupportedException e = new ActionNotSupportedException();
				String msg = "Only undergoing tasks support the COMPLETE action.";
				_log.error(msg, e);
				throw e;
			}
		}

		task.setStatus(status);
		task.setUpdatedTime(today);
		getDbUtil().update(task);

		if (task.getStatus() == BaseConst.LIFETASK_STATUS_COMPLETED) {
			// 任务完成，发出通知
			getNoticeManager().notifyForEntry(task, ActionType.COMPLETE);
		}

		if (_log.isDebugEnabled()) {
			_log.debug("The status of task " + task.getId()
					+ " was updated to " + status);
		}

		return task;
	}

	@Override
	public List getEntitiesOfOwner() throws ServerException,
			NotMatchedException, InvalidValueException {
		String hql = "from LifeTask where aspirantId=? and status<>? order by createdTime DESC";
		Object[] params = { getOwnerId(), BaseConst.LIFETASK_STATUS_DELETED };
		return getEntities(hql, params);
	}

	@Override
	public List getEntitiesOfParent(String parentId) throws ServerException,
			NotMatchedException, InvalidValueException {
		String hql = null;
		Object[] params = { parentId, getOwnerId(),
				BaseConst.LIFETASK_STATUS_DELETED };
		if (getParentClass() == LifeGoal.class) {
			hql = RdcQueryConst.HQL_TASKS_BY_GOAL;
			params = new Object[] { parentId, getOwnerId(),
					BaseConst.LIFETASK_STATUS_DELETED };
		}
		else if (getParentClass() == Aspirant.class) {
			hql = RdcQueryConst.HQL_TASKS_BY_USER;
			params = new Object[] { getOwnerId(),
					BaseConst.LIFETASK_STATUS_DELETED };
		}
		else {
			_log.error("Wrong parent class. It should LifeGoal or Aspirant.");
			throw new InvalidValueException();
		}
		return getEntities(hql, params);
	}

}
